﻿
using System;
using System.IO;
using MX.Core.Caching.Redis.Serialization;
using MX.Core.Caching.Redis.Configuration;

namespace MX.Core.Caching.Redis.Sider
{
	// A writer that's a bit more aware of the redis protocol than RedisWriter
	public class ProtocolWriter
	{
		 
		readonly static RedisWriter _writer = new RedisWriter ();
		readonly static ProtocolEncoder _encoder = new ProtocolEncoder ();


		public bool AutoFlush { get; set; }

		public ProtocolWriter ()
		{
			 
			AutoFlush = false;
		}


		public void WriteCmdStart (Stream _stream, string command, int numArgs)
		{
			_writer.WriteTypeChar (_stream, ResponseType.MultiBulk);
			_writer.WriteLine (_stream, numArgs + 1);

			_writer.WriteTypeChar (_stream, ResponseType.Bulk);
			_writer.WriteLine (_stream, command.Length);
			_writer.WriteLine (_stream, command);

			flushIfAuto (_stream);
		}

		public void WriteArg (Stream _stream, string data)
		{ 
			byte[] _buffer = new byte[BufferConfig.EncodingBufferSize];
			var arr = _encoder.Encode (_buffer,data); 
			writeBulkStart (_stream, arr.Count);
			_writer.WriteBulk (_stream, arr.Array, arr.Offset, arr.Count);
			flushIfAuto (_stream);
		}

		// TODO: Number serialization?
		public void WriteArg (Stream _stream, int num)
		{
			WriteArg (_stream, num.ToString ());
		}

		public void WriteArg (Stream _stream, long num)
		{
			WriteArg (_stream, num.ToString ());
		}

		public void WriteArg (Stream _stream, double num)
		{
			WriteArg (_stream, _encoder.Encode (num));
		}

		public void WriteArg (Stream _stream, Stream source, int count)
		{
			writeBulkStart (_stream, count);
			_writer.WriteBulkFrom (_stream, source, count);
			flushIfAuto (_stream);
		}

		public void WriteArg (Stream _stream, byte[] raw)
		{
			writeBulkStart (_stream, raw.Length);
			_writer.WriteBulk (_stream, raw);
			flushIfAuto (_stream);
		}

		public void WriteArg (Stream _stream, ArraySegment<byte> raw)
		{
			writeBulkStart (_stream, raw.Count);
			_writer.WriteBulk (_stream, raw.Array, raw.Offset, raw.Count);
			flushIfAuto (_stream);
		}

		public void WriteArg (Stream _stream, TimeSpan span)
		{
			WriteArg (_stream, _encoder.Encode (span));
		}

		public void WriteArg (Stream _stream, DateTime dt)
		{
			WriteArg (_stream, _encoder.Encode (dt));
		}


		public void Flush (Stream _stream)
		{
			_writer.Flush (_stream);
		}

		private void flushIfAuto (Stream _stream)
		{
			if (AutoFlush)
				Flush (_stream);
		}


		private void writeBulkStart (Stream _stream, int count)
		{
			_writer.WriteTypeChar (_stream, ResponseType.Bulk);
			_writer.WriteLine (_stream, count);
		}


	}
}
